//解析808协议
//赵亦平 2018.12.4

package protocol808

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

// 协议类别
const (
	PTTerminalAnswer  string = "0001" //终端通用应答
	PTServerAnswer    string = "8001" //平台通用应答
	PTRegister        string = "0100" //终端注册
	PTRegisterAnswer  string = "8100" //终端注册平台应答
	PTAuthentication  string = "0102" //终端鉴权
	PTHearbeat        string = "0002" //终端心跳
	PTPosition        string = "0200" //终端上报位置信息
	PTSetParameter    string = "8103" //设置终端参数
	PTGetParameter    string = "8104" //查询终端参数
	PTAnswerParameter string = "0104" //查询终端参数应答
	PTGetProperty     string = "8107" //查询终端属性
	PTAnswerProperty  string = "0107" //查询终端属性应答
	PTUploadPositions string = "0704" //定位数据批量上传
	PTTirePresure     string = "0900" //胎温胎压数据
	PTCommandDown     string = "8900" //数据下行透传
	PTRemoteUpdate    string = "8105" //设备远程OTA升级
)

// 标识位
const Flag string = "7E"

var ProtocalTypeMap map[string]string = map[string]string{
	PTTerminalAnswer:  "终端通用应答",
	PTServerAnswer:    "平台通用应答",
	PTRegister:        "终端注册",
	PTRegisterAnswer:  "终端注册平台应答",
	PTAuthentication:  "终端鉴权",
	PTHearbeat:        "终端心跳",
	PTPosition:        "终端上报位置信息",
	PTSetParameter:    "设置终端参数",
	PTGetParameter:    "查询终端参数",
	PTGetProperty:     "查询终端属性",
	PTAnswerProperty:  "查询终端属性应答",
	PTAnswerParameter: "查询终端参数应答",
	PTUploadPositions: "定位数据批量上传",
	PTTirePresure:     "胎温胎压数据",
	PTCommandDown:     "数据下行透传",
	PTRemoteUpdate:    "设备远程OTA升级",
}

// ----------------------------------------------------------------------------------------
// 消息头
type Header struct {
	MessageType         string //消息类别
	MessageTypeCH       string //消息类别中文名
	MessageAttribute    string //消息体属性
	TerminalId          string //终端ID
	MessageSerialNumber string //消息流水号
	CheckCode           string //校验码

	MultiplePackage bool //是否分包
	PackageBodySize int  //消息体长度
	PackageCount    int  //消息总包数
	PackageIndex    int  //消息包序号，从1开始
}

// 初始化消息头
func (p *Header) init() {
	p.MessageAttribute = ""
	p.MessageSerialNumber = ""
	p.TerminalId = ""
	p.MessageType = ""
	p.MessageTypeCH = ""
	p.CheckCode = ""

	p.MultiplePackage = false
	p.PackageBodySize = 0
	p.PackageCount = 0
	p.PackageIndex = 0
}

// 设置消息头
func (p *Header) set(text string) error {
	if len(text) != 24 {
		return errors.New("消息头长度不正确")
	}
	p.MessageType = text[:4]
	p.MessageAttribute = text[4:8]
	p.TerminalId = text[8:20]
	p.MessageSerialNumber = text[20:24]
	if v, ok := ProtocalTypeMap[p.MessageType]; ok {
		p.MessageTypeCH = v
	}

	att := str2Hex(p.MessageAttribute)
	p.MultiplePackage = (att>>13)&0x01 == 1
	p.PackageBodySize = att & 0x3FF

	return nil
}

// 生成字符串
func (p *Header) toStr() string {
	if p.MultiplePackage {
		return fmt.Sprintf("%s%04X%s%s", p.MessageType, p.PackageBodySize, p.TerminalId, p.MessageSerialNumber)
	} else {
		return p.MessageType + p.MessageAttribute + p.TerminalId + p.MessageSerialNumber
	}
}

// ----------------------------------------------------------------------------------------
// 终端注册
type Register struct {
	content string

	IMEI string //设备的IMEI号
}

func (p *Register) Set(text string) {
	p.content = text
	p.IMEI = ""

	iLen := len(text)
	if iLen > 30 {
		index := iLen - 30
		text = text[index:]
		//IMEI号为15个字节长度，16进制的字符串为30个字节
		p.IMEI = str2Ascii(text)
	}
}

// ----------------------------------------------------------------------------------------
// 终端鉴权
type Authentication struct {
	AuthCode string //鉴权码
}

func (p *Authentication) Set(text string) {
	p.AuthCode = text //[:4]
}

func (p *Authentication) Out() {
	fmt.Printf("鉴权码：\t%s\n", p.AuthCode)
}

// ----------------------------------------------------------------------------------------
// 终端通用应答
type TerminalCommonAnswer struct {
	content           string
	ServerSerialNo    int
	ServerMessageType string
	Result            int //0：成功/确认；1：失败；2：消息有误；3：不支持
}

func (p *TerminalCommonAnswer) Set(text string) {
	p.content = text
	//0001810501
	temp := text[:4]
	p.ServerSerialNo = str2Hex(temp)
	p.ServerMessageType = text[4:8]
	temp = text[8:]
	p.Result = str2Int(temp)
}

// ----------------------------------------------------------------------------------------
// 报警标志
type AlarmMark_t struct {
	content                string
	EmergencyFlag          int `bson:"emergency"` //紧急报警，触动报警开关后触发
	SpeedFlag              int `bson:"speed"`     //超速报警
	TiredFlag              int `bson:"tired"`     //疲劳驾驶
	Dangerous              int `bson:"dengerous"` //危险预警
	GNSSModuleFail         int `bson:"gnssfail"`  //GNSS模块发生故障
	GNSSAerialCuted        int `bson:"gnsscuted"` //GNSS天线未接或被剪断
	GNSSAerialShortCircuit int `bson:"gnssasc"`   //GNSS天线短路
	TerminalLowPower       int `bson:"lowpower"`  //终端主电源欠压
	TerminalNoPower        int `bson:"nopower"`   //终端主电源掉电
}

func (p *AlarmMark_t) set(str string) {
	p.content = str
	iContent := str2Hex(p.content)
	iFlag := 0x01

	p.EmergencyFlag = iFlag & iContent

	iFlag <<= 1
	p.SpeedFlag = iFlag & iContent >> 1

	iFlag <<= 1
	p.TiredFlag = iFlag & iContent >> 2

	iFlag <<= 1
	p.Dangerous = iFlag & iContent >> 3

	iFlag <<= 1
	p.GNSSModuleFail = iFlag & iContent >> 4

	iFlag <<= 1
	p.GNSSAerialCuted = iFlag & iContent >> 5

	iFlag <<= 1
	p.GNSSAerialShortCircuit = iFlag & iContent >> 6

	iFlag <<= 1
	p.TerminalLowPower = iFlag & iContent >> 7

	iFlag <<= 1
	p.TerminalNoPower = iFlag & iContent >> 8
}

func (p *AlarmMark_t) out() {
	fmt.Println("报警标志：\t", p.content)

	if p.EmergencyFlag == 0 {
		fmt.Println("\t无紧急报警")
	} else {
		fmt.Println("\t有紧急报警")
	}

	if p.SpeedFlag == 0 {
		fmt.Println("\t无超速报警")
	} else {
		fmt.Println("\t有超速报警")
	}
	if p.TiredFlag == 0 {
		fmt.Println("\t无疲劳驾驶")
	} else {
		fmt.Println("\t有疲劳驾驶")
	}
	if p.Dangerous == 0 {
		fmt.Println("\t无危险预警")
	} else {
		fmt.Println("\t有危险预警")
	}
	if p.GNSSModuleFail == 0 {
		fmt.Println("\tGNSS模块末发生故障")
	} else {
		fmt.Println("\tGNSS模块发生故障")
	}
	if p.GNSSAerialCuted == 0 {
		fmt.Println("\tGNSS天线连接正常")
	} else {
		fmt.Println("\tGNSS天线未接或被剪断")
	}
	if p.GNSSAerialShortCircuit == 0 {
		fmt.Println("\tGNSS天线末短路")
	} else {
		fmt.Println("\tGNSS天线短路")
	}
	if p.TerminalLowPower == 0 {
		fmt.Println("\t终端主电源电压正常")
	} else {
		fmt.Println("\t终端主电源欠压")
	}
	if p.TerminalNoPower == 0 {
		fmt.Println("\t终端主电源正常")
	} else {
		fmt.Println("\t终端主电源掉电")
	}

}

// 终端状态
type TerminalStatus_t struct {
	content string

	ACC            int `bson:"acc"`            //ACC开关，0：关，1：开
	Locate         int `bson:"located"`        //0：未定位，1：已定位
	Latitude       int `bson:"latflag"`        //0：北纬，1：南纬
	Longtitude     int `bson:"lngflag"`        //0：东经，1：西经
	Encrypt        int `bson:"encrytp"`        //0：经纬度未加密，1：已加密
	LCDOnline      int `bson:"lcdonline"`      //LCD屏是否在线，0:在线 ,1:离线
	ManualShutdown int `bson:"manualshutdown"` //是否按键关机，1表示当前按键关机
}

func (p *TerminalStatus_t) set(str string) {
	p.content = str

	iContent := str2Hex(str)

	p.ACC = iContent & 0x01
	p.Locate = iContent & 0x02 >> 1
	p.Latitude = iContent & 0x04 >> 2
	p.Longtitude = iContent & 0x08 >> 3
	p.Encrypt = iContent & 0x20 >> 5
	p.LCDOnline = iContent & 0x2000 >> 13
	p.ManualShutdown = iContent & 0x4000 >> 14
}

func (p *TerminalStatus_t) out() {
	fmt.Println("状态：\t", p.content)
	if p.ACC == 0 {
		fmt.Println("\tACC关")
	} else {
		fmt.Println("\tACC开")
	}

	if p.Locate == 0 {
		fmt.Println("\t未定位")
	} else {
		fmt.Println("\t已定位")
	}

	if p.Latitude == 0 {
		fmt.Println("\t北纬")
	} else {
		fmt.Println("\t南纬")
	}

	if p.Longtitude == 0 {
		fmt.Println("\t东经")
	} else {
		fmt.Println("\t西经")
	}

	if p.Encrypt == 0 {
		fmt.Println("\t经纬度未加密")
	} else {
		fmt.Println("\t经纬度已加密")
	}

	if p.LCDOnline == 0 {
		fmt.Println("\tLCD屏在线")
	} else {
		fmt.Println("\tLCD屏离线")
	}

	if p.ManualShutdown == 1 {
		fmt.Println("\t手动关机")
	}
}

// 位置附加信息
type TerminalPositionAddition struct {
	content           string  //内容
	GSMSignalStrength int     `bson:"gsmss"`   //GSM信号强度
	GPSSatelliteCount int     `bson:"gpssc"`   //GPS卫星个数
	GPSMileage        int     `bson:"gpsmile"` //GPS里程统计，单位公里
	PowerSource       int     `bson:"ps"`      //是否使用外接电源，1：外接，0：自备电源
	Voltage           float32 `bson:"vol"`     //电压
	ModuleStatus      int     `bson:"stat"`    //胎压模块是否正常，0：正常，-1：异常
}

func (p *TerminalPositionAddition) set(str string) {
	p.content = str

	for key, value := p.getAData(); key != ""; key, value = p.getAData() {
		if value != "" {
			switch key {
			case "30":
				//GSM信号强度
				p.GSMSignalStrength = str2Hex(value)
				//break
			case "31":
				//GPS卫星个数
				p.GPSSatelliteCount = str2Hex(value)
				//break
			case "FE":
				//GPS里程统计
				p.GPSMileage = str2Hex(value)
				//break
			case "05":
				//是否外接电源
				p.PowerSource = str2Hex(value)
				//break
			case "06":
				//电池电压
				iTemp := str2Int(value)
				if iTemp > 420 {
					iTemp = 420
				}
				p.Voltage = (float32(iTemp)) / 100
				//break
			case "08":
				//胎压模块是否正常
				if value == "01" {
					//正常
					p.ModuleStatus = 0
				} else {
					//异常
					p.ModuleStatus = -1
				}
			}
		}
	}
}

/*
	func (p *TerminalPositionAddition) getValue(key string) string {
		index := strings.Index(p.content, key)
		if index < 0 {
			return ""
		}
		index += len(key)
		if index >= len(p.content) {
			return ""
		}
		sTemp := p.content[index : index+2]
		iTemp := str2Int(sTemp) * 2
		index += 2
		if index+iTemp > len(p.content) {
			return ""
		}
		sTemp = p.content[index : index+iTemp]
		return sTemp
	}
*/
func (p *TerminalPositionAddition) getAData() (key, value string) {
	//字符串起码要大于4，2字节的标识，2字节的数据长度
	if len(p.content) <= 4 {
		return "", ""
	}

	key = p.content[0:2]
	sValueLen := p.content[2:4]
	iValueLen := str2Int(sValueLen) * 2
	if iValueLen+4 > len(p.content) {
		return "", ""
	}

	value = p.content[4 : 4+iValueLen]
	p.content = p.content[iValueLen+4:]

	return
}

func (p *TerminalPositionAddition) out() {
	//fmt.Println("附加信息：", p.content)
	fmt.Println("GMS信号强度：\t", p.GSMSignalStrength)
	fmt.Println("GPS卫星个数：\t", p.GPSSatelliteCount)
	fmt.Println("GPS里程：\t", p.GPSMileage)
	fmt.Println("电池电压：\t", p.Voltage)
	if p.PowerSource == 0 {
		fmt.Println("外接电源：\t否")
	} else {
		fmt.Println("外接电源：\t是")
	}
	if p.ModuleStatus == 0 {
		fmt.Println("胎压模块正常")
	} else {
		fmt.Println("胎压模块异常")
	}
}

// 终端数据上报位置信息
type TerminalPosition struct {
	content          string                   //内容
	AlarmMark        AlarmMark_t              `bson:"alarm"`  //报警标志
	TerminalStatus   TerminalStatus_t         `bson:"stat"`   //状态定义
	Latitude         float32                  `bson:"lat"`    //纬度
	Longitude        float32                  `bson:"lng"`    //经度
	Height           int                      `bson:"height"` //高度，单位：米
	Speed            float32                  `bson:"speed"`  //速度，单位：公里/小时
	Direction        int                      `bson:"dir"`    //方向，正北为0，顺时针0-359
	Date             int                      `bson:"date"`   //格式：YYMMDD
	Time             int                      `bson:"time"`   //格式：HHNNSS
	PositionAddition TerminalPositionAddition `bson:"add"`    //位置附加信息
}

func (p *TerminalPosition) Set(text string) {
	p.content = text

	var index int = 0
	var sTemp string = ""
	var iTemp int = 0

	//报警标志
	p.AlarmMark.set(text[index : index+8])

	//状态
	index += 8
	p.TerminalStatus.set(text[index : index+8])

	//纬度
	index += 8
	sTemp = text[index : index+8]
	iTemp = str2Hex(sTemp)
	p.Latitude = float32(iTemp) / 1000000

	//经度
	index += 8
	sTemp = text[index : index+8]
	iTemp = str2Hex(sTemp)
	p.Longitude = float32(iTemp) / 1000000

	//海拔
	index += 8
	sTemp = text[index : index+4]
	p.Height = str2Hex(sTemp)

	//速度
	index += 4
	sTemp = text[index : index+4]
	iTemp = str2Hex(sTemp)
	p.Speed = float32(iTemp) / 10

	//方向
	index += 4
	sTemp = text[index : index+4]
	p.Direction = str2Hex(sTemp)

	//日期
	index += 4
	sTemp = text[index : index+6]
	p.Date = str2Int(sTemp)

	//时间
	index += 6
	sTemp = text[index : index+6]
	p.Time = str2Int(sTemp)

	//附加消息
	index += 6
	sTemp = text[index:]
	p.PositionAddition.set(sTemp)
}

func (p *TerminalPosition) Out() {
	p.AlarmMark.out()
	p.TerminalStatus.out()
	fmt.Println("纬度：\t", p.Latitude)
	fmt.Println("经度：\t", p.Longitude)
	fmt.Println("高度：\t", p.Height, "米")
	fmt.Println("速度：\t", p.Speed, "公里/小时")
	fmt.Println("方向：\t", p.Direction)
	fmt.Println("日期：\t", p.Date)
	fmt.Println("时间：\t", p.Time)
	p.PositionAddition.out()
}

// ----------------------------------------------------------------------------------------
// 单个轮胎的数据
type TireData_t struct {
	content                       string
	CardId                        int     `bson:"cardid"` //卡号
	SerialNum                     int     `bson:"sn"`     //序号
	SensorId                      string  `bson:"sid"`    //传感器ID
	Voltage                       float32 `bson:"vol"`    //传感器电压
	ExplosionProofDeviceInstalled bool    `bson:"epdins"` //防爆装置已安装
	ExplosionProofDeviceLoosed    bool    `bson:"epdlos"` //防爆装置已松脱
	Presure                       float32 `bson:"pres"`   //气压，单位为Bar
	Temperature                   int     `bson:"temp"`   //温度
	PresureStatus                 int     `bson:"pstat"`  //气压状态，0：正常；1：急漏气；2：慢漏气；3：加气
	TemperatureStatus             int     `bson:"tstat"`  //气温状态，0：温度正常；1：温度高
	PresureHighOrLow              int     `bson:"phl"`    //气压高低，0：气压正常；1：气压高；2：气压低
	MoveStatus                    int     `bson:"ms"`     //运动状态，0：传感器为静止状态；1：传感器为运动状态
	NoData                        int     `bson:"nodata"` //当长时间（60 分钟）没有收到发射器的数据后此位置 1，(此时忽略压力温度状态字节)
	VoltageStatus                 int     `bson:"vstat"`  //发射器电池电压状态，0 表示电池电压正常，1 表示电池电压低
	Learned                       int
}

func (p *TireData_t) set(str string) {
	var index int = 0
	var sTemp string = ""
	var iTemp int = 0

	p.content = str

	//卡号
	sTemp = p.content[index : index+2]
	iTemp = str2Hex(sTemp)
	p.CardId = 0xE0 & iTemp >> 5

	//序号
	p.SerialNum = 0x1F & iTemp

	//传感器ID
	index += 2
	p.SensorId = p.content[index : index+6]

	//传感器电压
	index += 6
	sTemp = p.content[index : index+2]
	iTemp = str2Hex(sTemp)
	p.Voltage = float32(0xF0&iTemp>>4)/10 + 1.9
	p.ExplosionProofDeviceInstalled = (iTemp & 0x08 >> 3) == 1
	p.ExplosionProofDeviceLoosed = (iTemp & 0x04 >> 2) == 1

	//气压
	var iPresure int = iTemp & 0x03 << 8
	index += 2
	sTemp = p.content[index : index+2]
	iTemp = str2Hex(sTemp)
	iPresure += iTemp
	p.Presure = float32(iPresure) * 0.025

	//温度
	index += 2
	sTemp = p.content[index : index+2]
	iTemp = str2Hex(sTemp)
	p.Temperature = iTemp - 50
	if p.Temperature == -50 {
		p.Temperature = 0
	}

	//报警信息
	index += 2
	sTemp = p.content[index : index+2]
	iTemp = str2Hex(sTemp)

	p.PresureStatus = iTemp & 0x03

	iTemp = iTemp >> 2
	p.TemperatureStatus = iTemp & 0x01

	iTemp = iTemp >> 1
	if iTemp&0x01 == 1 {
		p.PresureHighOrLow = 2
	}

	iTemp = iTemp >> 1
	if iTemp&0x01 == 1 {
		p.PresureHighOrLow = 1
	}

	iTemp = iTemp >> 1
	p.MoveStatus = iTemp & 0x01

	iTemp = iTemp >> 1
	p.NoData = iTemp & 0x01

	iTemp = iTemp >> 1
	p.VoltageStatus = iTemp & 0x01
}

func (p *TireData_t) out() {
	fmt.Println("\t卡号：", p.CardId)
	fmt.Println("\t序号：", p.SerialNum)
	fmt.Println("\t传感器ID：", p.SensorId)
	fmt.Println("\t传感器电压：", p.Voltage)
	if p.ExplosionProofDeviceInstalled {
		fmt.Println("\t防爆装置已安装")
	} else {
		fmt.Println("\t防爆装置末安装")
	}
	if p.ExplosionProofDeviceLoosed {
		fmt.Println("\t防爆装置已松脱")
	} else {
		fmt.Println("\t防爆装置末松脱")
	}
	fmt.Println("\t气压：", p.Presure, " Bar")
	fmt.Println("\t温度：", p.Temperature, "摄氏度")

	var sTemp string

	switch p.PresureStatus {
	case 0:
		sTemp = "正常"
	case 1:
		sTemp = "急漏气"
	case 2:
		sTemp = "慢漏气"
	case 3:
		sTemp = "加气"
	}
	fmt.Println("\t气压状态：", sTemp)
	if p.TemperatureStatus == 0 {
		fmt.Println("\t气温状态：", "温度正常")
	} else {
		fmt.Println("\t气温状态：", "温度高")
	}
	if p.PresureHighOrLow == 0 {
		sTemp = "气压正常"
	} else if p.PresureHighOrLow == 1 {
		sTemp = "气压高"
	} else if p.PresureHighOrLow == 2 {
		sTemp = "气压低"
	} else {
		sTemp = ""
	}
	fmt.Println("\t气压高低：", sTemp)
	if p.MoveStatus == 0 {
		sTemp = "传感器为静止状态"
	} else if p.MoveStatus == 1 {
		sTemp = "传感器为运动状态"
	} else {
		sTemp = "传感器运动状态不正确"
	}
	fmt.Println("\t运动状态：", sTemp)
	if p.NoData == 1 {
		fmt.Println("\t长期未收到数据，传感器可能已失联")
	}
	if p.VoltageStatus == 0 {
		fmt.Println("\t发射器电池电压正常")
	} else {
		fmt.Println("\t发射器电池电压低")
	}
}

// 胎压数据
type TirePressure struct {
	content string

	TranId    string       //数据透传ID，根据ID进行不同的数据解析
	IdCH      string       //ID对应中文
	Model     string       //胎压传感器设备型号或制造商
	PageCount int          //总页数
	Page      int          //当前页
	TireCount int          //轮胎个数
	Datas     []TireData_t //轮胎数据
	Latitude  float32      //纬度
	Longitude float32      //经度
	Date      int          //格式：yymmdd
	Time      int          //格式：hhnnss
}

func NewTirePressure() *TirePressure {
	tp := TirePressure{}
	tp.Datas = make([]TireData_t, 0)
	return &tp
}

func (p *TirePressure) Set(str string) {
	p.content = str
	p.Datas = p.Datas[:0]

	index := 0
	sTemp := ""
	iTemp := 0

	p.TranId = p.content[index : index+2]
	switch p.TranId {
	case "F1":
		p.IdCH = "米其林"
	case "F2":
		p.IdCH = "永奥图"
	case "F3":
		p.IdCH = "车辆对码信息"
	default:
		p.IdCH = "末定义"
	}

	//设备型号
	index += 2
	p.Model = p.content[index : index+2]

	//总页数
	index += 2
	sTemp = p.content[index : index+2]
	p.PageCount = str2Int(sTemp)

	//当前页
	index += 2
	sTemp = p.content[index : index+2]
	p.Page = str2Int(sTemp)

	//轮胎个数
	index += 2
	sTemp = p.content[index : index+2]
	p.TireCount = str2Hex(sTemp)

	//逐一读出每个轮胎
	index += 2
	for i := 0; i < p.TireCount; i++ {
		sTemp = p.content[index : index+16]
		tp := TireData_t{}
		tp.set(sTemp)
		p.Datas = append(p.Datas, tp)
		index += 16
	}

	//不是F1/F2就不需要继续解析了
	if p.TranId != "F1" && p.TranId != "F2" {
		return
	}

	//纬度
	sTemp = p.content[index : index+8]
	iTemp = str2Hex(sTemp)
	p.Latitude = float32(iTemp) / 1000000

	//经度
	index += 8
	sTemp = p.content[index : index+8]
	iTemp = str2Hex(sTemp)
	p.Longitude = float32(iTemp) / 1000000

	//日期
	index += 8
	sTemp = p.content[index : index+6]
	p.Date = str2Int(sTemp)

	//时间
	index += 6
	sTemp = p.content[index : index+6]
	p.Time = str2Int(sTemp)
}

func (p *TirePressure) Out() {
	fmt.Println("透传ID：\t", p.TranId, "(", p.IdCH, ")")
	fmt.Println("设备型号：\t", p.Model)
	fmt.Println("总页数：\t", p.PageCount)
	fmt.Println("当前页：\t", p.Page)
	fmt.Println("轮胎个数：\t", p.TireCount)
	for i := 0; i < p.TireCount; i++ {
		fmt.Println("\t-----------第", i+1, "个轮胎-----------")
		p.Datas[i].out()
	}
	fmt.Println("纬度：\t", p.Latitude)
	fmt.Println("经度：\t", p.Longitude)
	fmt.Println("日期：\t", p.Date)
	fmt.Println("时间：\t", p.Time)
}

// ----------------------------------------------------------------------------------------
// 查询终端属性应答
type AnswerProperty struct {
	content string

	TerminalType          int    //终端类型
	ManufacturerId        string //制造商ID
	TerminalModel         string //终端型号
	TerminalId            string //终端ID
	ICCID                 string //Sim卡的ICCID号
	HardwareVersionLength int    //终端硬件版本号长度
	HardwareVersion       string //终端硬件版本号
	FirmwareVersionLength int    //终端固件版本号长度
	FirmwareProjectCode   string //终端固件项目代码项目
	FirmwareVersion       string //终端固件版本号
	*AnswerProperty
	TPMSModuleVersion string
}

func (p *AnswerProperty) Set(text string) {
	p.content = text

	var index int = 0
	var sTemp string = ""
	//var iTemp int = 0

	//终端类型
	sTemp = text[index : index+4]
	p.TerminalType = str2Hex(sTemp)

	//制造商ID
	index += 4
	p.ManufacturerId = text[index : index+10]

	//终端型号
	index += 10
	p.TerminalModel = text[index : index+40]

	//终端ID
	index += 40
	p.TerminalId = text[index : index+14]

	//Sim卡的ICCID号
	index += 14
	p.ICCID = text[index : index+20]

	//终端硬件版本号长度
	index += 20
	sTemp = text[index : index+2]
	p.HardwareVersionLength = str2Hex(sTemp) * 2

	//终端硬件版本号
	index += 2
	sTemp = text[index : index+p.HardwareVersionLength]
	p.HardwareVersion = str2Ascii(sTemp)

	//终端固件版本号长度
	index += p.HardwareVersionLength
	sTemp = text[index : index+2]
	p.FirmwareVersionLength = str2Hex(sTemp) * 2

	//终端固件项目代码
	index += 2
	sTemp = text[index : index+p.FirmwareVersionLength]
	sTemp = str2Ascii(sTemp)
	//终端固件版本号
	index = strings.Index(sTemp, "V")
	if index >= 0 {
		p.FirmwareProjectCode = sTemp[0:index]
		p.FirmwareVersion = sTemp[index+1:]
	}
}

func (p *AnswerProperty) Out() {
	fmt.Println("ICCID：\t", p.ICCID)
	fmt.Println("终端硬件版本号：\t", p.HardwareVersion)
	fmt.Println("终端固件项目代码：\t", p.FirmwareProjectCode)
	fmt.Println("终端固件版本号：\t", p.FirmwareVersion)
}

// ----------------------------------------------------------------------------------------
// 查询终端参数应答
const (
	//设备参数标识
	ParameterTagServerIP             string = "00000013" //服务器地址
	ParameterTagServerPort           string = "00000018" //服务器端口
	ParameterTagGPSInterval          string = "00000029" //GPS上传间隔
	ParameterTagTirePressureInterval string = "0000F101" //胎压上传间隔
	ParameterTagDeviceSleep          string = "00000027" //设备休眠机制,0：不休眠，1：休眠
	ParameterTagSetScreen            string = "00000028"
)

type AnswerParamenter struct {
	content string

	ParameterCount       int    //参数总数
	ServerIP             string //服务器地址
	ServerPort           int    //服务器端口
	GPSInterval          int    //GPS上传间隔
	TirePressureInterval int    //胎压上传间隔
	DeviceSleep          int    //设备休眠机制,0：不休眠，1：休眠
	Voice                int
	TemperatureUnit      int
	PressureUnit         int
	VehicleType          int
}

func (p *AnswerParamenter) Set(text string) {
	p.content = text

	var index int = 0
	var iLen int = 0
	var iData int = 0
	var sTemp string = ""

	const TagLength int = len(ParameterTagServerIP)
	//跳过应答流水号
	index += 4
	//参数总数
	sTemp = text[index : index+2]
	p.ParameterCount = str2Hex(sTemp)

	//服务器地址
	index = strings.Index(text, ParameterTagServerIP)
	if index > 0 {
		index += TagLength
		sTemp = text[index : index+2]
		iLen = str2Hex(sTemp)
		index += 2
		p.ServerIP = str2Ascii(text[index : index+(iLen*2)])
	}
	//服务器端口
	index = strings.Index(text, ParameterTagServerPort)
	if index > 0 {
		index += TagLength + 2 //跳过2个字节的长度
		sTemp = text[index : index+8]
		p.ServerPort = str2Hex(sTemp)
	}
	//GPS上传间隔
	index = strings.Index(text, ParameterTagGPSInterval)
	if index > 0 {
		index += TagLength + 2 //跳过2个字节的长度
		sTemp = text[index : index+8]
		p.GPSInterval = str2Hex(sTemp)
	}
	//胎压上传间隔
	index = strings.Index(text, ParameterTagTirePressureInterval)
	if index > 0 {
		index += TagLength
		sTemp = text[index : index+2]
		iLen = str2Hex(sTemp)
		index += 2
		sTemp = text[index : index+(iLen*2)]
		p.TirePressureInterval = str2Hex(sTemp)
	}
	//设备休眠
	index = strings.Index(text, ParameterTagDeviceSleep)
	if index > 0 {
		index += TagLength
		sTemp = text[index : index+2]
		iLen = str2Hex(sTemp)
		index += 2
		sTemp = text[index : index+(iLen*2)]
		p.DeviceSleep = str2Hex(sTemp)
	}
	index = strings.Index(text, ParameterTagSetScreen)
	if index > 0 {
		index += TagLength
		sTemp = text[index : index+2]
		iLen = str2Hex(sTemp)
		index += 2
		sTemp = text[index : index+(iLen*2)]
		iData = str2Hex(sTemp)
		p.Voice = iData & 0x0F
		iData >>= 4
		p.TemperatureUnit = iData & 0x0F
		iData >>= 4
		p.PressureUnit = iData & 0x0F
		iData >>= 4
		p.VehicleType = iData & 0x0F
	}
}

func (p *AnswerParamenter) Out() {
	fmt.Println("服务器地址：\t", p.ServerIP)
	fmt.Println("服务器端口：\t", p.ServerPort)
	fmt.Println("GPS上传间隔：\t", p.GPSInterval)
	fmt.Println("胎压上传间隔：\t", p.TirePressureInterval)
	if p.DeviceSleep == 0 {
		fmt.Println("设备不休眠")
	} else {
		fmt.Println("设备会休眠")
	}
	fmt.Println("声音开关：\t", p.Voice)
	fmt.Println("温度单位：\t", p.TemperatureUnit)
	fmt.Println("压力单位：\t", p.PressureUnit)
	fmt.Println("车辆类别：\t", p.VehicleType)
}

// ----------------------------------------------------------------------------------------
// 808协议请求解析类
type Request struct {
	header Header
	body   string
}

// 解析
func (p *Request) Parse(text string) (error, *Header, string) {
	p.header.init()
	p.body = ""

	//检查标识位
	err := p.checkFlag(text)
	if err != nil {
		return err, nil, ""
	}

	//先对字符串中的7D进行转义
	text = p.transfer(text)

	index := 26
	iLen := len(text)
	//解析消息头
	if err = p.header.set(text[2:index]); err != nil {
		return err, nil, ""
	}

	//判断是否分包
	if p.header.MultiplePackage {
		p.header.PackageCount = str2Hex(text[index : index+4])
		index += 4
		p.header.PackageIndex = str2Hex(text[index : index+4])
		index += 4
	}

	p.body = text[index : iLen-4]

	//检查消息体长度
	if err = p.checkBodyLength(); err != nil {
		return err, nil, ""
	}

	p.header.CheckCode = text[iLen-4 : iLen-2]

	return nil, &p.header, p.body
}

// 生成消息字符串
func (p *Request) Pack(header *Header, body string) string {
	res := header.toStr() + body
	iCheckCode := sum(res)
	res += int2HexStr(int(iCheckCode), "%02X")

	return Flag + p.transfer(res) + Flag
}

// 7D转义：7D->7D01, 7E->7D02
func (p *Request) transfer(str string) string {
	for i := len(str) - 6; i >= 2; i -= 2 {
		if str[i] == '7' && str[i+1] == 'D' {
			if str[i+2] == '0' {
				if str[i+3] == '1' {
					str = str[:i+2] + str[i+4:]
				} else if str[i+3] == '2' {
					str = str[:i] + "7E" + str[i+4:]
				}
			}
		}
	}

	return str
}

// 检查标识位
func (p *Request) checkFlag(text string) error {
	s := text[:2]
	if s != Flag {
		return errors.New("非法数据包")
	} else {
		s = text[len(text)-2:]
		if s != Flag {
			return errors.New("非法数据包")
		}
	}

	return nil
}

// 检查消息体长度
func (p *Request) checkBodyLength() error {
	if p.header.MessageType == PTHearbeat {
		//心跳数据包的长度为0
		return nil
	}
	if p.header.PackageBodySize*2 != len(p.body) {
		return errors.New("消息体长度不正确")
	} else {
		return nil
	}
}

// ----------------------------------------------------------------------------------------
// 808协议请求回复类
const (
	ResultSuccess      string = "00"
	ResultFail         string = "01"
	ResultWrong        string = "02"
	ResultNotSupport   string = "03"
	ResultConfirmAlarm string = "04" //报警处理确认
)

// 通用应答
type Response struct {
	header Header

	MessageType        string //消息ID
	SerialNo           uint16 //本条消息流水号
	RequestSeriaNo     string //应答流水号，即终端请求时的流水号
	RequestMessageType string //应答消息的类别
	TerminalId         string //终端ID号
	Result             string //结果:0：成功/确认；1：失败；2：消息有误；3：不支持；4：报警处理确认
}

// 生成消息字符串
func (p *Response) Pack() string {
	text := p.RequestSeriaNo + p.RequestMessageType + p.Result

	p.header.MessageType = p.MessageType
	p.header.MessageAttribute = int2HexStr(len(text)/2, "%04X")
	p.header.TerminalId = p.TerminalId
	p.header.MessageSerialNumber = int2HexStr(int(p.SerialNo), "%04X")

	res := p.header.toStr() + text
	iCheckCode := sum(res)
	res += int2HexStr(int(iCheckCode), "%02X")

	return Flag + p.transfer(res) + Flag
}

// 转义7E/7D，字符串不包含开头和结尾的标识位
func (p *Response) transfer(str string) string {
	// for i := len(str) - 6; i >= 2; i -= 2 {
	for i := len(str) - 2; i >= 0; i -= 2 {
		if str[i] == '7' {
			if str[i+1] == 'E' {
				str = str[:i] + "7D02" + str[i+2:]
			} else if str[i+1] == 'D' {
				str = str[:i] + "7D01" + str[i+2:]
			}
		}
	}

	return str
}

// 注册消息应答
type ResponseRegister struct {
	Response
	AuthCode string //鉴权码
}

func (p *ResponseRegister) Pack() string {
	text := p.RequestSeriaNo + p.Result + p.AuthCode

	p.header.MessageType = PTRegisterAnswer
	p.header.MessageAttribute = int2HexStr(len(text)/2, "%04X")
	p.header.TerminalId = p.TerminalId
	p.header.MessageSerialNumber = int2HexStr(int(p.SerialNo), "%04X")

	res := p.header.toStr() + text
	iCheckCode := sum(res)
	res += int2HexStr(int(iCheckCode), "%02X")

	return Flag + p.transfer(res) + Flag
}

// ----------------------------------------------------------------------------------------
// 808协议下发指令类
// 子指令类型
const (
	CommandIdMichelinSetTirePressure string = "F1" //米其林-设置胎压(高压/低压/高温/低电压)门限
	CommandIdYatSetTirePressure      string = "F2" //永奥图-设置胎压(高压/低压/高温/低电压)门限
	CommandIdBindTireToPosition      string = "F3" //设置胎压传感器 ID 和轮位绑定
	CommandIdQueryThreshold          string = "F4" //查询全车轴设置胎压温度阀值
)

// 操作模式
const CommandOperateModelQueryThreshold string = "00" //1 表示查询
const CommandOperateModelSet string = "01"            //1:绑定轮位与传感器ID
const CommandOperateModelDelete string = "02"         //2:删除轮位与传感器ID的关系
const CommandOperateModelQuery string = "03"          //3:查询轮位与传感器ID的关系
const CommandOperateModelMatchImmediately = "04"      //4:立刻绑定轮位与传感器ID
const CommandOperateModelMatchAuto = "05"             //5:自动绑定轮位与传感器ID

// 指令类
type Command struct {
	header Header

	//MessageType string //消息ID
	SerialNo  uint16 //本条消息流水号
	CommandId string //两个字节长度的字符串：F1/F2/F3/F4
}

// 永奥图胎压胎温阀值处理
type YatTirePressureAndTemperatureThresholdT struct {
	Pressures   [5][2]float32 //5轴胎压阀值，前高后低
	Temperature int           //高温阀值
}

func (p *YatTirePressureAndTemperatureThresholdT) toStr() string {
	res := ""

	for i := 0; i < 5; i++ {
		res += int2HexStr(int(p.Pressures[i][0]*10), "%02X") + int2HexStr(int(p.Pressures[i][1]*10), "%02X")
	}
	res += int2HexStr(p.Temperature+50, "%02X")
	return res
}

type CommandYatTirePressure struct {
	Command

	operateModel string //操作模式：01
	axisCount    int    //轴总数
	TerminalId   string //设备ID
	Threshold    YatTirePressureAndTemperatureThresholdT
}

// 解析消息内容
func (p *CommandYatTirePressure) Parse(text string) error {
	index := 0
	sTemp := ""
	iLen := len(text)
	if iLen != 28 {
		//消息长度必须是28个字节
		return errors.New("消息体长度不正确，应该为28个字节，实际为" + strconv.Itoa(iLen))
	}
	sTemp = text[:2]
	if sTemp != "F2" {
		//不是合法的胎压胎温阀值消息
		return errors.New("不是合法的胎压胎温阀值消息")
	}
	index += 4 //跳过透传ID、操作模式，这些都是固定值

	//轴总数
	sTemp = text[index : index+2]
	iAxisCount := str2Int(sTemp)
	for i := 0; i < iAxisCount; i++ {
		index += 2
		sTemp = text[index : index+2]
		p.Threshold.Pressures[i][0] = float32(str2Hex(sTemp)) * 0.1
		index += 2
		sTemp = text[index : index+2]
		p.Threshold.Pressures[i][1] = float32(str2Hex(sTemp)) * 0.1
	}
	index += 2
	sTemp = text[index : index+2]
	p.Threshold.Temperature = str2Hex(sTemp) - 50 //温度阀值=参数值-50

	return nil
}

// 查询设备的阀值设置
func (p *CommandYatTirePressure) Query(terminalId string) (message, commandText string) {
	p.operateModel = CommandOperateModelQueryThreshold
	p.CommandId = CommandIdQueryThreshold
	p.TerminalId = terminalId

	text := p.CommandId

	p.header.MessageType = PTCommandDown
	p.header.MessageAttribute = int2HexStr(len(text)/2, "%04X")
	p.header.TerminalId = p.TerminalId
	p.header.MessageSerialNumber = int2HexStr(int(p.SerialNo), "%04X")

	res := p.header.toStr() + text
	iCheckCode := sum(res)
	res += int2HexStr(int(iCheckCode), "%02X")

	return Flag + transfer(res) + Flag, text
}

// 数据打包
func (p *CommandYatTirePressure) Pack() (message, commandText string) {
	p.CommandId = CommandIdYatSetTirePressure
	p.operateModel = CommandOperateModelSet
	p.axisCount = 5

	commandText = fmt.Sprintf("%s%s%02d%s", p.CommandId, p.operateModel, p.axisCount, p.Threshold.toStr())

	p.header.MessageType = PTCommandDown
	p.header.MessageAttribute = int2HexStr(len(commandText)/2, "%04X")
	p.header.TerminalId = p.TerminalId
	p.header.MessageSerialNumber = int2HexStr(int(p.SerialNo), "%04X")

	message = p.header.toStr() + commandText
	iCheckCode := sum(message)
	message += int2HexStr(int(iCheckCode), "%02X")
	message = Flag + transfer(message) + Flag

	return
}

// 米其林设置标准胎压命令
type CommandMichelinSetTirePressure struct {
	Command

	operateModel  string  //操作模式：01
	axisCount     int     //轴总数
	TerminalId    string  //设备ID
	Axis1Pressure float64 //第一轴标准压力，单位0.1bar，3.2bar转换为20
	Axis2Pressure float64 //第二轴标准压力，同上
}

// 返回完整消息体和指令
func (p *CommandMichelinSetTirePressure) Pack() (message string, command string) {
	p.operateModel = CommandOperateModelSet
	p.CommandId = CommandIdMichelinSetTirePressure
	if p.Axis1Pressure != 0 {
		p.axisCount++
	}
	if p.Axis2Pressure != 0 {
		p.axisCount++
	}

	press1 := (int)(p.Axis1Pressure * 10)
	press2 := (int)(p.Axis2Pressure * 10)

	text := fmt.Sprintf("%s%s%02d%02X%02X", p.CommandId, p.operateModel, p.axisCount, press1, press2)

	p.header.MessageType = PTCommandDown
	p.header.MessageAttribute = int2HexStr(len(text)/2, "%04X")
	p.header.TerminalId = p.TerminalId
	p.header.MessageSerialNumber = int2HexStr(int(p.SerialNo), "%04X")

	res := p.header.toStr() + text
	iCheckCode := sum(res)
	res += int2HexStr(int(iCheckCode), "%02X")

	return Flag + transfer(res) + Flag, text
}

// 绑定轮位指令
type SensorPositionT struct {
	SensorId string //胎压传感器 ID，最大占 4 个字节(FFFFFFFF)，如果只用三个字节前面补 0 如：(00FFFFFF)，删除模式时为空
	CardId   int    //00～07 有效
	SerialNo int    //01～20 有效，1-32
}

func (p *SensorPositionT) toStr() string {
	var str string

	if p.SensorId == "" {
		str = fmt.Sprintf("%02X%02X", p.CardId, p.SerialNo)
	} else {
		str = fmt.Sprintf("%08s%02X%02X", p.SensorId, p.CardId, p.SerialNo)
	}
	return str
}

type CommandBindSensorToPosition struct {
	Command

	TerminalId   string            //设备ID
	OperateModel string            //操作模式
	Sensors      []SensorPositionT //轮胎数组
}

func (p *CommandBindSensorToPosition) Pack() (message string, command string) {
	text := fmt.Sprintf("%s%s%02X", p.CommandId, p.OperateModel, len(p.Sensors))
	for _, v := range p.Sensors {
		text += v.toStr()
	}

	p.header.MessageType = PTCommandDown
	p.header.MessageAttribute = int2HexStr(len(text)/2, "%04X")
	p.header.TerminalId = p.TerminalId
	p.header.MessageSerialNumber = int2HexStr(int(p.SerialNo), "%04X")

	res := p.header.toStr() + text
	iCheckCode := sum(res)
	res += int2HexStr(int(iCheckCode), "%02X")

	return Flag + transfer(res) + Flag, text
}

// 修改服务器地址指令
type CommandSetServer struct {
	Command

	TerminalId string //设备ID
	ServerIP   string //服务器IP或域名
	ServerPort int    //服务器端口号
}

func (p *CommandSetServer) Pack() (message string, command string) {
	parameterCount := 2
	text := fmt.Sprintf("%02X%s%02X%s%s%02X%08X", parameterCount, ParameterTagServerIP, len(p.ServerIP), ascii2HexStr(p.ServerIP),
		ParameterTagServerPort, 4, p.ServerPort)

	p.header.MessageType = PTSetParameter
	p.header.MessageAttribute = int2HexStr(len(text)/2, "%04X")
	p.header.TerminalId = p.TerminalId
	p.header.MessageSerialNumber = int2HexStr(int(p.SerialNo), "%04X")

	res := p.header.toStr() + text
	iCheckCode := sum(res)
	res += int2HexStr(int(iCheckCode), "%02X")

	return Flag + transfer(res) + Flag, text
}

// 设置数据上传频率，单位为秒
type CommandSetUploadFrequncy struct {
	Command

	TerminalId       string //设备ID
	TireDataInterval int    //胎温胎压上传间隔，单位为秒
	GPSInterval      int    //定位数据上传频率，单位为秒
}

func (p *CommandSetUploadFrequncy) Pack() (message, command string) {
	parameterCount := 2

	if p.GPSInterval < 5 {
		p.GPSInterval = 5 //最小5秒
	} else if p.GPSInterval > 43200 {
		p.GPSInterval = 43200 //最大12小时
	}

	if p.TireDataInterval < 10 {
		p.TireDataInterval = 10 //最小10秒
	} else if p.TireDataInterval > 43200 {
		p.TireDataInterval = 43200 //最大12小时
	}

	text := fmt.Sprintf("%02X%s04%08X%s02%04X", parameterCount, ParameterTagGPSInterval, p.GPSInterval,
		ParameterTagTirePressureInterval, p.TireDataInterval)

	p.header.MessageType = PTSetParameter
	p.header.MessageAttribute = int2HexStr(len(text)/2, "%04X")
	p.header.TerminalId = p.TerminalId
	p.header.MessageSerialNumber = int2HexStr(int(p.SerialNo), "%04X")

	res := p.header.toStr() + text
	iCheckCode := sum(res)
	res += int2HexStr(int(iCheckCode), "%02X")

	return Flag + transfer(res) + Flag, text
}

// 设置设备是否休眠
type CommandSetDeviceSleep struct {
	Command

	TerminalId  string //设备ID
	DeviceSleep int    //设备是否要休眠，0：不休眠，1：休眠
}

func (p *CommandSetDeviceSleep) Pack() (message, command string) {
	parameterCount := 1

	if p.DeviceSleep != 0 {
		p.DeviceSleep = 1
	}

	text := fmt.Sprintf("%02X%s02%04X", parameterCount, ParameterTagDeviceSleep, p.DeviceSleep)

	p.header.MessageType = PTSetParameter
	p.header.MessageAttribute = int2HexStr(len(text)/2, "%04X")
	p.header.TerminalId = p.TerminalId
	p.header.MessageSerialNumber = int2HexStr(int(p.SerialNo), "%04X")

	res := p.header.toStr() + text
	iCheckCode := sum(res)
	res += int2HexStr(int(iCheckCode), "%02X")

	return Flag + transfer(res) + Flag, text
}

type CommandSetScreen struct {
	Command

	TerminalId      string
	Voice           int
	TemperatureUnit int
	PressureUnit    int
	VehicleType     int
}

func (p *CommandSetScreen) Pack() (message, command string) {
	parameterCount := 1
	data := p.VehicleType
	data <<= 4
	data |= p.PressureUnit
	data <<= 4
	data |= p.TemperatureUnit
	data <<= 4
	data |= p.Voice
	text := fmt.Sprintf("%02X%s02%04X", parameterCount, ParameterTagSetScreen, data)
	p.header.MessageType = PTSetParameter
	p.header.MessageAttribute = int2HexStr(len(text)/2, "%04X")
	p.header.TerminalId = p.TerminalId
	p.header.MessageSerialNumber = int2HexStr(int(p.SerialNo), "%04X")

	res := p.header.toStr() + text
	iCheckCode := sum(res)
	res += int2HexStr(int(iCheckCode), "%02X")

	return Flag + transfer(res) + Flag, text
}

// ----------------------------------------------------------------------------------------
// 设备远程OTA升级指令
type RemoteUpdate struct {
	header Header

	TerminalId      string //设备号
	SerialNo        uint16 //本条消息流水号
	Url             string //固件地址
	Productor       string //制造商：SZRXT
	FirmwareVersion string //固件版本，RXTmt2503aV1.0
	SoftwareVersion string //软件版本，308V1.0
}

func (p *RemoteUpdate) Pack() (string, string) {
	p.header.MessageType = PTRemoteUpdate
	p.header.MessageSerialNumber = int2HexStr(int(p.SerialNo), "%04X")
	p.header.TerminalId = p.TerminalId

	//http://tracker.resiont.com/web/download/V1.0.4.vxp;;;;;;;SZRXT;RXTmt2503aeV20;203V1.0.4;0
	const UpdateStrFormat string = "%s;;;;;;;%s;%s;%s;"
	// hexUrl := ascii2HexStr(p.Url)
	// hexProductor := ascii2HexStr(p.Productor)
	// hexFirmwareVersion := ascii2HexStr(p.FirmwareVersion)
	// hexSoftwareVersion := ascii2HexStr(p.SoftwareVersion)

	updateStr := fmt.Sprintf(UpdateStrFormat, p.Url, p.Productor, p.FirmwareVersion, p.SoftwareVersion)
	updateStr = "01" + ascii2HexStr(updateStr)

	p.header.MessageAttribute = int2HexStr(len(updateStr)/2, "%04X")

	res := p.header.toStr() + updateStr
	iCheckCode := sum(res)
	res += int2HexStr(int(iCheckCode), "%02X")

	return Flag + transfer(res) + Flag, updateStr
}

// ----------------------------------------------------------------------------------------
// 辅助函数
// 字符串转16进制
func str2Hex(str string) int {
	if str == "" {
		return 0
	}
	i, err := strconv.ParseInt(str, 16, 0)
	if err != nil {
		return 0
	} else {
		return int(i)
	}
}

// 字符串转数字
func str2Int(str string) int {
	if str == "" {
		return 0
	}
	i, err := strconv.ParseInt(str, 10, 0)
	if err != nil {
		return 0
	} else {
		return int(i)
	}
}

// 整型转16进制字符串
func int2HexStr(val int, format string) string {
	return fmt.Sprintf(format, val)
}

// 将字符串转成ASCII码
func str2Ascii(text string) string {
	res := make([]byte, 0)
	var sTemp string
	var iTemp int

	for i := 0; i < len(text); i += 2 {
		sTemp = text[i : i+2]
		iTemp = str2Hex(sTemp)
		res = append(res, byte(iTemp))
	}
	return string(res)
}

// 将ASCII码字符串转化为16进制的字符串
func ascii2HexStr(text string) string {
	var res string
	var temp string
	for _, v := range text {
		temp = fmt.Sprintf("%X", v)
		res += temp
	}
	return res
}

// 计算检验码
func sum(text string) uint8 {
	if text == "" {
		return 0
	}
	var res uint8
	var sTemp string
	var iTemp int

	for i := 0; i < len(text); i += 2 {
		sTemp = text[i : i+2]
		iTemp = str2Hex(sTemp)
		if i == 0 {
			res = uint8(iTemp)
		} else {
			res ^= uint8(iTemp)
		}
	}
	return res
}

// 转义7E/7D--> 7D02/7D01，字符串不包含开始和结尾的标识位
func transfer(str string) string {
	// for i := len(str) - 6; i >= 2; i -= 2 {
	for i := len(str) - 2; i >= 0; i -= 2 {
		if str[i] == '7' {
			if str[i+1] == 'E' {
				str = str[:i] + "7D02" + str[i+2:]
			} else if str[i+1] == 'D' {
				str = str[:i] + "7D01" + str[i+2:]
			}
		}
	}

	return str
}

//----------------------------------------------------------------------------------------
